package com.songaw.generator.modules.auths.security;

import com.songaw.generator.common.conf.redis.CacheLoadback;
import com.songaw.generator.common.conf.redis.RedisUtil;
import com.songaw.generator.common.constant.AuthConstant;
import com.songaw.generator.common.constant.Constant;
import com.songaw.generator.modules.auths.pojo.dto.AuthUserDto;
import com.songaw.generator.modules.auths.pojo.dto.RoleDto;
import com.songaw.generator.modules.auths.service.AuthService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    @Lazy
    AuthService authService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Value("${swagger.show}")
    private  boolean swaggerShow;
    @Value("${jwt.header}")
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;
    //    token失效时间为5分钟
    @Value("${jwt.expiration}")
    private Long expiration;
    //token失效后 中间间隔2分钟是有效的
    @Value("${jwt.refresh_token_expiration}")
    private Long refreshTokenExpiration;
    //redis中token失效时间为1小时
    @Value("${jwt.redis_token_expiration}")
    private Long redisTokenExpiration;

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain) throws ServletException, IOException {
        if ("OPTIONS".equals(request.getMethod())) {
            HttpServletResponse resq = (HttpServletResponse) response;
            resq.setStatus(HttpStatus.OK.value());
            return;
        }
        String url=request.getRequestURI();
        if(!swaggerShow){
            if(url.indexOf("swagger")>-1){
                HttpServletResponse resq = (HttpServletResponse) response;
                resq.setStatus(HttpStatus.OK.value());
                return;
            }
        }


       // System.out.println(url);
        String authHeader = request.getHeader(this.tokenHeader);

        if (authHeader == null || authHeader.equals("undefined")) {
            authHeader = request.getParameter(this.tokenHeader);
        }

        if (authHeader != null && authHeader.startsWith(tokenHead)) {

            //截取token
            final String authToken = authHeader.substring(tokenHead.length()); // The part after "Bearer "
           try {
               //获得token对应的username
               String username = jwtTokenUtil.getSubject(authToken);

               // logger.info("checking authentication " + username);

               if (username != null ) {
                   if (jwtTokenUtil.validateToken(authToken, username)) {
                       setSecurityContext(request, username);
                   } else {
                       //刷新token逻辑
                       String refreshToken = authService.refresh(authToken, username);
                       if (refreshToken != null) {
                           if (setSecurityContext(request, username)) {
                               response.addHeader(tokenHeader, tokenHead + refreshToken);
                           }
                       }
                   }
               }
           } catch (InternalAuthenticationServiceException var8) {
               this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
               this.unsuccessfulAuthentication(request, response, var8);
               return;
           } catch (AuthenticationException var9) {
               this.unsuccessfulAuthentication(request, response, var9);
               return;
           }
        }
        if(url.startsWith("/admin")){
            if(SecurityContextHolder.getContext().getAuthentication() == null){
                request.getRequestDispatcher("/index.html").forward(request, response);
            }else{
                Authentication auth = SecurityContextHolder.getContext().getAuthentication();
                if (auth != null){
                    Object o= auth.getPrincipal();
                    if(o!=null&&!(o instanceof String)) {
                        try {
                            AuthUserDto userDetails = new AuthUserDto();
                            BeanUtils.copyProperties(o, userDetails);
                            List<RoleDto> roleDtos = userDetails.getRoles();
                            if (!CollectionUtils.isEmpty(roleDtos)) {
                                for(int i=0;i<roleDtos.size();i++){
                                    if (AuthConstant.ROLE_ADMIN.equals(roleDtos.get(i).getAuthority())) {
                                        chain.doFilter(request, response);
                                        return;
                                    }
                                }
                            }
                        }catch (Exception e){
                            logger.info(e.getMessage());

                        }
                    }
                }
                try {
                    request.getRequestDispatcher("/index.html").forward(request, response);
                }catch (Exception e){
                    logger.info(e.getMessage());
                }
                return;
            }
        }
        chain.doFilter(request, response);
    }
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
        SecurityContextHolder.clearContext();

        if (logger.isDebugEnabled()) {
            logger.debug("Authentication request failed: " + failed.toString());
            logger.debug("Updated SecurityContextHolder to contain null Authentication");
            // logger.debug("Delegating to authentication failure handler " + failureHandler);
        }

        // rememberMeServices.loginFail(request, response);

        failureHandler.onAuthenticationFailure(request, response, failed);
    }
    private boolean setSecurityContext(HttpServletRequest request, String username) {
        //从缓存中获取
        UserDetails userDetails = null;
        try {
            userDetails = RedisUtil.get(Constant.CODE_CACHE_USERNAME + username, redisTokenExpiration, new CacheLoadback<UserDetails>() {
                @Override
                public UserDetails load() {
                    return userDetailsService.loadUserByUsername(username);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (userDetails != null) {
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
                    request));
            //   logger.info("authenticated user " + username + ", setting security context");
            SecurityContextHolder.getContext().setAuthentication(authentication);
            return true;
        }
        return false;
    }
}
